package edu.wpi.cs509.representation;

import static org.junit.Assert.*;

import org.junit.Test;
import java.util.*;
import java.util.logging.Logger;

public class ActionTest1 {

	private static final int debug_level = 10;
	Game game;  // the game
	Gameboard gb; // the gameboard

	/**
	 * The main test routine.
	 * Select an attacker and defender
	 * then sends these to the move/battle routine
	 */
	@Test(timeout=5000)
	public void testTheAction() {

		Queue<PlayerPiece> theAttackerPieceList = new LinkedList<PlayerPiece>();
		Queue<PlayerPiece> theDefenderPieceList = new LinkedList<PlayerPiece>();

		// generate game, gameboard
		game = new Game();
		gb = game.getGameboard();

		//list the type of pieces to test
		StrategoPiece[] sPieceList = {
				StrategoPiece.MARSHAL,
				StrategoPiece.GENERAL,
				StrategoPiece.COLONEL,
				StrategoPiece.MAJOR,
				StrategoPiece.CAPTAIN, 
				StrategoPiece.LIEUTENANT,
				StrategoPiece.SERGEANT,
				StrategoPiece.MINER,
				StrategoPiece.SCOUT,
				StrategoPiece.SPY,
				StrategoPiece.BOMB,
				StrategoPiece.FLAG
		};

		// for each piece type get a reference to the
		// actual piece on the gameboard (red and blue)
		// red will be the attacker
		for (StrategoPiece sPiece : sPieceList) {
			theAttackerPieceList.add(gb.getPlayerPieceInBoneyard(TeamColor.RED,  sPiece));
			theDefenderPieceList.add(gb.getPlayerPieceInBoneyard(TeamColor.BLUE, sPiece));
		}

		// run an attack for each permutation of attacking and defending piece 
		for (PlayerPiece attPiece : theAttackerPieceList) {
			for (PlayerPiece defPiece : theDefenderPieceList) {
				doAnAttack( attPiece , defPiece );
			}
		}
	}

	/**
	 * doAnAttack - given an attacking and defending piece - perform an attack
	 * @param attPiece - the attacking piece
	 * @param defPiece - the defending piece
	 */
	private void doAnAttack(PlayerPiece attPiece, PlayerPiece defPiece) {

		boolean expInvalidMove = false;
		boolean expGameEnd = false;
		boolean aWins = false;
		boolean aLoses = false;
		boolean dWins = false;
		boolean dLoses = false;

		// predict outcome of the move/attack...

		// attacker isn't supposed to be able to move/attack		
		if (attPiece.maxSpacesCanMove()==0) {
			expInvalidMove=true; expGameEnd=false; aWins=false; aLoses=false; dWins=false; dLoses=false;
		}

		// piece being attacked is flag (results in end of game)
		else if (defPiece.getPieceType() == StrategoPiece.FLAG) {
			expInvalidMove=false; expGameEnd=true;  aWins=true; aLoses=false; dWins=false; dLoses=true;
		}

		// attacker can kill bomb, and defender is bomb
		else if (attPiece.canKillaBomb() && defPiece.isaBomb()) {
			expInvalidMove=false; expGameEnd=false; aWins=true; aLoses=false; dWins=false; dLoses=true;
		}
		// attacker can kill marshal, and defender is marshal
		else if (attPiece.canKillaMarshal() && defPiece.isaMarshal()) {
			expInvalidMove=false; expGameEnd=false; aWins=true; aLoses=false; dWins=false; dLoses=true;
		}
		// defender is a bomb
		else if (defPiece.isaBomb()){
			expInvalidMove=false; expGameEnd=false; aWins=false; aLoses=true; dWins=true; dLoses=false;
		}
		// rank are the same (both lose)
		else if (attPiece.getRank() == defPiece.getRank()) {
			expInvalidMove=false; expGameEnd=false; aWins=false; aLoses=true; dWins=false; dLoses=true;
		}
		// attacker outranks (is lower rank than) attacker
		else if (attPiece.getRank() < defPiece.getRank()) {
			expInvalidMove=false; expGameEnd=false; aWins=true; aLoses=false; dWins=false; dLoses=true;
		}
		// defender outranks (is lower rank than) attacker
		else if (defPiece.getRank() < attPiece.getRank()){
			expInvalidMove=false; expGameEnd=false; aWins=false; aLoses=true; dWins=true; dLoses=false;
		}
		else {
			fail("100.0 should not get here");
		}

		Logger.getLogger("global").info("----- "+attPiece.getName()+" attacking "+defPiece.getName()+" --- "+"expInvalidMove "+expInvalidMove+" expGameEnd "+expGameEnd+" aWins "+aWins+" aLoses "+aLoses+" dWins "+dWins+" dLoses "+dLoses+" -----");


		// add the pieces on their respective side of board
		vanillaAdd("attacking piece", attPiece, 0, 3);
		vanillaAdd("defending piece", defPiece, 0, 6);

		// move attacking piece up to defending piece
		vanillaMove("move", attPiece, 0, 4, expInvalidMove);

		// if the move was expected to be invalid
		// aka the piece could not be moved then
		// the test is done - remove the pieces
		if (expInvalidMove) {
			vanillaRemove("remove",attPiece);
			vanillaRemove("remove",defPiece);	
		}

		// the piece can move so continue on with the
		// moves and attack
		else {
			// move attacking piece up to defending piece
			vanillaMove("move", attPiece, 0, 5, false);

			// attack
			vanillaAttack("attack", attPiece, 0, 6, defPiece, expInvalidMove, expGameEnd, aWins, aLoses, dWins, dLoses);

			// clean up (remove any remaining pieces)
			if (aWins) {
				vanillaRemove("remove",attPiece);
			}
			if (dWins) {
				vanillaRemove("remove",defPiece);	
			}
		}
		Logger.getLogger("global").info("- DONE -");
	}

	/**
	 * add a piece to the board (move from graveyard to gameboard)
	 * @param string - descriptive string
	 * @param pce - reference to the piece being moved
	 * @param x - target x-coordinate
	 * @param y - target y-coordinate
	 */
	private void vanillaAdd(String string, PlayerPiece pce, int x, int y) {
		Location loc = null;
		ActionAdd aAdd;

		Logger.getLogger("global").info("--- vanillaAdd  "+pce.getName()+"["+x+","+y+"] "+string);

		try {
			loc = gb.getBoardsquare(x, y);
		} catch (GameboardException e) {
			e.printStackTrace();
			fail("200.0 - caught an exception");
		}

		aAdd = new ActionAdd("add", gb, pce, loc);
		try {
			aAdd.execute();
		} catch (ActionNotLegalException e) {
			e.printStackTrace();
			fail("200.1 - caught an ActionNotLegalException - not expected");
		}
		catch (ActionEndOfGameException e) {
			e.printStackTrace();
			fail("200.2 - caught an ActionEndOfGameException - not expected");
		}
	}

	/**
	 * vanillaMove - move from one location on gameboard to another
	 * @param string - descriptive string
	 * @param pce - the piece being moved
	 * @param x - destination x coordinate
	 * @param y - destination y coordinate
	 * @param expInvalid - expect an invalid move exception
	 */
	private void vanillaMove(String string, PlayerPiece pce, int x, int y, boolean expInvalid) {
		Location loc = null;
		ActionMove aMove;

		Logger.getLogger("global").info("--- vanillaMove "+pce.getName()+"["+x+","+y+"] "+string);

		try {
			loc = gb.getBoardsquare(x, y);
		} catch (GameboardException e) {
			e.printStackTrace();
			fail("210.0 - caught an exception");
		}

		aMove = new ActionMove("move", gb, pce, loc);
		try {
			aMove.execute();
			// expected exception (not received)
			if (expInvalid) {
				fail("210.1 - expected an ActionNotLegalException - not received");	
			}
		} catch (ActionNotLegalException e) {
			// received Exception (not expected)
			if (!expInvalid) {
				e.printStackTrace();
				fail("210.2 - caught an ActionNotLegalException - not expected");
			}
		} catch (ActionEndOfGameException e) {
			// received Exception (not expected)
			e.printStackTrace();
			fail("210.3 - caught an ActionEndOfGameException - not expected");
		}
	}


	/**
	 * vanillaAttack - perform a move which is expected to be an attack
	 * @param string - a descriptive string
	 * @param aPce - piece moving (performing the attack)
	 * @param x - x coordinate being move to
	 * @param y - y coordinate being moved to
	 * @param dPce - defending piece
	 * @param expInvalid - expect an invalid move exception
	 * @param expEndGame - expect an end-of-game exception
	 * @param aWin - attacker is expected to win
	 * @param aLose - attacker is expected to lose
	 * @param dWin - defender is expected to win
	 * @param dLose - defender is expected to lose
	 */
	private void vanillaAttack(
			String string,
			PlayerPiece aPce, int x, int y,
			PlayerPiece dPce,
			boolean expInvalid, boolean expEndGame,
			boolean aWin, boolean aLose,
			boolean dWin, boolean dLose) {
		Location loc = null;
		ActionMove aMove;

		Logger.getLogger("global").info("--- vanillaAttack "+aPce.getName()+"["+x+","+y+"] "+dPce.getName()+"["+x+","+y+"] "+string);

		// get boardsquare to move to
		try {
			loc = gb.getBoardsquare(x, y);
		} catch (GameboardException e) {
			e.printStackTrace();
			fail("220.0 - caught an exception");
		}

		if (debug_level > 5) {
			Logger.getLogger("global").info("zona" + loc.getYCoordinate()+loc.getXCoordinate()+aPce.getLocation().getYCoordinate()+aPce.getLocation().getYCoordinate());

		}

		// debug message
		if (debug_level > 5) {
			Logger.getLogger("global").info("ActionTest1() (before move)"+" aPce "+aPce.getLocation().getYCoordinate()+aPce.getLocation().getXCoordinate()+" loc " +loc.getYCoordinate()+loc.getXCoordinate());			
		}

		// perform the move
		aMove = new ActionMove("attack", gb, aPce, loc);
		try {
			aMove.execute();
			// expected exception (not received)
			if (expInvalid) {
				fail("220.1 - expected an ActionNotLegalException - not received");	
			}
			// expected exception (not received)
			if (expEndGame) {
				fail("220.2 - expected an ActionEndOfGameException - not received");	
			}
		} catch (ActionNotLegalException e) {
			// received Exception (not expected)
			if (!expInvalid) {
				e.printStackTrace();
				fail("220.3 - caught an ActionNotLegalException - not expected");
			}
		} catch (ActionEndOfGameException e) {
			// received Exception (not expected)
			if (!expEndGame) {
				e.printStackTrace();
				fail("220.4 - caught an ActionEndOfGameException - not expected");
			}
		}

		// debug message
		if (debug_level > 5) {
			Logger.getLogger("global").info("ActionTest1() (after move)"+" aPce "+aPce.getLocation()+" loc " +loc);			
		}

		// if attacker wins
		if (aWin) {

			// check the piece has moved to the new location
			try {
				loc = gb.getBoardsquare(x, y);
			} catch (GameboardException e) {
				e.printStackTrace();
				fail("220.5 - caught an exception");
			}
			if (aPce.getLocation() != loc) {
				fail("220.6 - attacker won the battle, but didn't end up in proper location");
			}
		}
		// attacker has lost
		if (aLose) {
			// check that it ended up in the boneyard
			loc = gb.getBoneyard();
			if (aPce.getLocation() != loc) {
				fail("220.7 - attacker lost the battle but did not end up in boneyard");
			}
		}




		// if defender wins
		if (dWin) {
			// winning defender does not change location
		}
		// defender has lost
		if (dLose) {
			// check that it ended up in the boneyard
			loc = gb.getBoneyard();
			if (dPce.getLocation() != loc) {
				fail("220.a - defender lost the battle but did not end up in boneyard");
			}
		}
	}	

	/**
	 * vanillaRemove - removed a piece from the gameboard
	 * @param string - descriptive string
	 * @param pce - piece to be removed
	 */
	private void vanillaRemove(String string, PlayerPiece pce) {
		Location loc = null;
		ActionRemove aRemove;

		Logger.getLogger("global").info("--- vanillaRemove "+pce.getName()+string);

		loc = gb.getBoneyard();
		aRemove = new ActionRemove("remove", gb, pce, loc);
		try {
			aRemove.execute();
		} catch (ActionNotLegalException e) {
			e.printStackTrace();
			fail("230.0 - caught an exception");
		} catch (ActionEndOfGameException e) {
			e.printStackTrace();
			fail("230.1 - caught an exception");
		}
	}
}


